package mymail.crypttwice;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.Key;
import java.util.Arrays;
import java.util.Date;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import mymail.Hash;
import mymail.cryptography.asymmetric.RSA;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class SymmetricThenRSA {

	private Key publicKey;
	private Key privateKey;

	public Key getPublicKey() {
		return publicKey;
	}

	public void setPublicKey(Key publicKey) {
		this.publicKey = publicKey;
	}

	public Key getPrivateKey() {
		return privateKey;
	}

	public void setPrivateKey(Key privateKey) {
		this.privateKey = privateKey;
	}
	
	

	public SymmetricThenRSA() {
		super();
		this.publicKey = null;
		this.privateKey = null;
	}



	private static String TRANSFORMATION_AES = "AES";
	private static String TRANSFORMATION_DES = "DES/ECB/PKCS5Padding";
	private static String TRANSFORMATION_DESEDE = "DESede/CBC/PKCS5Padding";

	// Mã hóa với text
	public String Encrypt(String password, String data, String algorithm)
			throws Exception {

		RSA rsa = new RSA();
		if(privateKey != null && publicKey != null){
			rsa.setPrivateKey(privateKey);
			rsa.setPublicKey(publicKey);
		}
		
		String encryptedData;

		// Tạo byte password
		byte[] bytesOfPassword = null;
		IvParameterSpec ip = null;

		if (algorithm.equals("AES")) {
			bytesOfPassword = Hash.hashData(password);
		} else if (algorithm.equals("DES")) {
			bytesOfPassword = password.getBytes("UTF-8");
		} else if (algorithm.equals("DESede")) {

			byte[] bytesOfPassword8 = password.getBytes("UTF-8");
			bytesOfPassword = Arrays.copyOf(bytesOfPassword8, 24);
			for (int j = 0, k = 16; j < 8;) {
				bytesOfPassword[k++] = bytesOfPassword[j++];
			}
			ip = new IvParameterSpec(password.getBytes("UTF-8"));
		}

		// Tạo key từ byte password
		Key key = new SecretKeySpec(bytesOfPassword, algorithm);

		// Mã hóa data với key được tạo từ byte password
		String transformation = TRANSFORMATION_AES;
		if (algorithm.equals("DES")) {
			transformation = TRANSFORMATION_DES;
		} else if (algorithm.equals("DESede")) {
			transformation = TRANSFORMATION_DESEDE;
		}
		Cipher c = Cipher.getInstance(transformation);
		if (algorithm.equals("AES") || algorithm.equals("DES")) {
			c.init(Cipher.ENCRYPT_MODE, key);
		} else if (algorithm.equals("DESede")) {
			c.init(Cipher.ENCRYPT_MODE, key, ip);
		}
		byte[] encryptedValue = c.doFinal(data.getBytes("UTF-8"));
		encryptedData = new BASE64Encoder().encode(encryptedValue);

		encryptedData = rsa.Encrypt(encryptedData);
		publicKey = rsa.getPublicKey();
		privateKey = rsa.getPrivateKey();

		return encryptedData;
	}

	// Giải mã với text
	public static String Decrypt(String password, String encryptedData,
			String algorithm, String privateKeyFilename) throws Exception {
		String data;

		encryptedData = RSA.Decrypt(encryptedData, privateKeyFilename);

		// Tạo byte password
		byte[] bytesOfPassword = null;
		IvParameterSpec ip = null;

		if (algorithm.equals("AES")) {
			bytesOfPassword = Hash.hashData(password);
		} else if (algorithm.equals("DES")) {
			bytesOfPassword = password.getBytes("UTF-8");
		} else if (algorithm.equals("DESede")) {

			byte[] bytesOfPassword8 = password.getBytes("UTF-8");
			bytesOfPassword = Arrays.copyOf(bytesOfPassword8, 24);
			for (int j = 0, k = 16; j < 8;) {
				bytesOfPassword[k++] = bytesOfPassword[j++];
			}
			ip = new IvParameterSpec(password.getBytes("UTF-8"));
		}

		// Tạo key từ byte password
		Key key = new SecretKeySpec(bytesOfPassword, algorithm);

		// Mã hóa data với key được tạo từ byte password
		String transformation = TRANSFORMATION_AES;
		if (algorithm.equals("DES")) {
			transformation = TRANSFORMATION_DES;
		} else if (algorithm.equals("DESede")) {
			transformation = TRANSFORMATION_DESEDE;
		}
		Cipher c = Cipher.getInstance(transformation);
		if (algorithm.equals("AES") || algorithm.equals("DES")) {
			c.init(Cipher.DECRYPT_MODE, key);
		} else if (algorithm.equals("DESede")) {
			c.init(Cipher.DECRYPT_MODE, key, ip);
		}
		byte[] decryptedValue = c.doFinal(new BASE64Decoder()
				.decodeBuffer(encryptedData));
		data = new String(decryptedValue);

		return data;
	}

	// Mã hóa với file

	@SuppressWarnings("deprecation")
	public String EncryptFile(String filename, String password, String algorithm)
			throws Exception {

		RSA rsa = new RSA();
		if(privateKey != null && publicKey != null){
			rsa.setPrivateKey(privateKey);
			rsa.setPublicKey(publicKey);
		}

		// Tạo byte password
		byte[] bytesOfPassword = null;
		IvParameterSpec ip = null;

		if (algorithm.equals("AES")) {
			bytesOfPassword = Hash.hashData(password);
		} else if (algorithm.equals("DES")) {
			bytesOfPassword = password.getBytes("UTF-8");
		} else if (algorithm.equals("DESede")) {

			byte[] bytesOfPassword8 = password.getBytes("UTF-8");
			bytesOfPassword = Arrays.copyOf(bytesOfPassword8, 24);
			for (int j = 0, k = 16; j < 8;) {
				bytesOfPassword[k++] = bytesOfPassword[j++];
			}
			ip = new IvParameterSpec(password.getBytes("UTF-8"));
		}

		// Tạo key từ byte password
		Key key = new SecretKeySpec(bytesOfPassword, algorithm);

		// Mã hoá
		String transformation = TRANSFORMATION_AES;
		if (algorithm.equals("DES")) {
			transformation = TRANSFORMATION_DES;
		} else if (algorithm.equals("DESede")) {
			transformation = TRANSFORMATION_DESEDE;
		}
		Cipher c = Cipher.getInstance(transformation);
		if (algorithm.equals("AES") || algorithm.equals("DES")) {
			c.init(Cipher.ENCRYPT_MODE, key);
		} else if (algorithm.equals("DESede")) {
			c.init(Cipher.ENCRYPT_MODE, key, ip);
		}

		FileInputStream fis = new FileInputStream(filename);
		int last = filename.lastIndexOf(".");
		File directory = new File(".");
		String name = directory.getCanonicalPath()
				+ "/temp/"
				+ (new Date()).toLocaleString().replace(" ", "")
						.replace(":", "").replace(",", "")
				+ filename.substring(last);

		File f;
		f = new File(name);
		f.createNewFile();

		FileOutputStream fos = new FileOutputStream(name);
		CipherOutputStream cos = new CipherOutputStream(fos, c);

		byte[] block = new byte[32];
		int i;
		while ((i = fis.read(block)) != -1) {
			cos.write(block, 0, i);
		}
		cos.close();
		
		rsa.EncryptFile(name);
		publicKey = rsa.getPublicKey();
		privateKey = rsa.getPrivateKey();

		return name;
	}

	// Giải mã với file
	public static void DecryptFile(String inputFilename, String outputFilename,
			String password, String algorithm, String privateKeyFilename)
			throws Exception {

		RSA.DecryptFile(inputFilename, outputFilename, privateKeyFilename);

		// Tạo byte password
		byte[] bytesOfPassword = null;
		IvParameterSpec ip = null;

		if (algorithm.equals("AES")) {
			bytesOfPassword = Hash.hashData(password);
		} else if (algorithm.equals("DES")) {
			bytesOfPassword = password.getBytes("UTF-8");
		} else if (algorithm.equals("DESede")) {

			byte[] bytesOfPassword8 = password.getBytes("UTF-8");
			bytesOfPassword = Arrays.copyOf(bytesOfPassword8, 24);
			for (int j = 0, k = 16; j < 8;) {
				bytesOfPassword[k++] = bytesOfPassword[j++];
			}
			ip = new IvParameterSpec(password.getBytes("UTF-8"));
		}

		// Tạo key từ byte password
		Key key = new SecretKeySpec(bytesOfPassword, algorithm);

		// Giải mã
		String transformation = TRANSFORMATION_AES;
		if (algorithm.equals("DES")) {
			transformation = TRANSFORMATION_DES;
		} else if (algorithm.equals("DESede")) {
			transformation = TRANSFORMATION_DESEDE;
		}
		Cipher c = Cipher.getInstance(transformation);
		if (algorithm.equals("AES") || algorithm.equals("DES")) {
			c.init(Cipher.DECRYPT_MODE, key);
		} else if (algorithm.equals("DESede")) {
			c.init(Cipher.DECRYPT_MODE, key, ip);
		}

		String extension = outputFilename.substring(outputFilename
				.lastIndexOf(".") + 1);
		String name = outputFilename.substring(0,
				outputFilename.lastIndexOf("."));
		String newOutputFilename = name + "_output_2" + extension;

		FileInputStream fis = new FileInputStream(outputFilename);
		CipherInputStream cis = new CipherInputStream(fis, c);
		FileOutputStream fos = new FileOutputStream(newOutputFilename);

		byte[] block = new byte[32];
		int i;
		while ((i = cis.read(block)) != -1) {
			fos.write(block, 0, i);
		}
		fos.close();

	}
}
